<?php
declare(strict_types=1);

namespace ExpressionEngine\LexicalAnalysisEngine\Loop;

use Contract\Exceptions\ValidationException;
use ExpressionEngine\LexicalAnalysisEngine\Enum\LexicalAnalysisEnum;
use ExpressionEngine\LexicalAnalysisEngine\LexicalAnalysisTag;
use ExpressionEngine\LoopEngine\Enum\LoopEnum;
use ExpressionEngine\VariableEngine\VariableEngineInterface;

class LexicalAnalysisLoopForeach implements LexicalAnalysisLoopInterface
{
    protected VariableEngineInterface $variableEngine;
    protected LexicalAnalysisTag $lexicalAnalysisTag;

    public function __construct(VariableEngineInterface $variableEngine, LexicalAnalysisTag $lexicalAnalysisTag)
    {
        $this->variableEngine = $variableEngine;
        $this->lexicalAnalysisTag = $lexicalAnalysisTag;
    }

    public function is(string $expression): bool
    {
        if (str_starts_with($expression, LoopEnum::LOOP_FOREACH_NAME)) {
            return true;
        }
        return false;
    }

    /**
     * @param string $expression
     * @return void
     * @throws ValidationException
     */
    public function validate(string $expression): void
    {
        if (!str_contains($expression, ':')) {
            throw new ValidationException("foreach 结构体格式不正确，缺少冒号");
        }
        if (substr_count($expression, ':') != 1) {
            throw new ValidationException("foreach 结构体格式不正确，存在多个冒号");
        }
        $this->validateHeader($this->parseHeader($expression));
    }

    /**
     * @param string $header
     * @return void
     * @throws ValidationException
     */
    protected function validateHeader(string $header): void
    {
        if (!str_contains($header, "(")) {
            throw new ValidationException("foreach 结构体格式不正确，头部缺少左括号");
        }
        if (substr_count($header, '(') > 1) {
            throw new ValidationException("foreach 结构体格式不正确，头部左括号数量大于1");
        }
        if (!str_contains($header, ")")) {
            throw new ValidationException("foreach 结构体格式不正确，头部缺少有括号");
        }
        if (substr_count($header, ')') > 1) {
            throw new ValidationException("foreach 结构体格式不正确，头部右括号数量大于1");
        }
        if (strpos($header, '(') > strpos($header, ')')) {
            throw new ValidationException("foreach 结构体格式不正确，左括号不能在右括号右边");
        }

        $headerMain = $this->parseHeaderMain($header);
        $symbols = explode(" ", $headerMain);
        $count = count($symbols);
        if ($count != 5) {
            throw new ValidationException('foreach 结构体格式不正确 header符号个数不正确:' . $count);
        }

        if (!$this->variableEngine->is($symbols[0])) {
            throw new ValidationException('foreach 结构体格式不正确 header头第1个符号不是变量:' . $symbols[0]);
        }
        if ($symbols[1] != LoopEnum::LOOP_FOREACH_AS) {
            throw new ValidationException('foreach 结构体格式不正确 header头第2个符号不正确:' . $symbols[1]);
        }
        if (!$this->variableEngine->is($symbols[2])) {
            throw new ValidationException('foreach 结构体格式不正确 header头第3个不是变量:' . $symbols[2]);
        }
        if ($symbols[3] != LoopEnum::LOOP_FOREACH_ARROW) {
            throw new ValidationException('foreach 结构体格式不正确 header头第3个符号不正确:' . $symbols[3]);
        }
        if (!$this->variableEngine->is($symbols[4])) {
            throw new ValidationException('foreach 结构体格式不正确 header头第3个不是变量:' . $symbols[2]);
        }
    }

    /**
     * @param string $expression
     * @return array
     */
    protected function parseHeaderData(string $expression): array
    {
        $header = $this->parseHeader($expression);
        $header = trim($header);
        $headerMain = $this->parseHeaderMain($header);
        $symbols = explode(" ", $headerMain);
        return [
            LoopEnum::LOOP_FOREACH_HEADER_TYPE_NAME => LoopEnum::LOOP_FOREACH_NAME,
            LoopEnum::LOOP_FOREACH_HEADER_LIST_NAME => $this->variableEngine->get($symbols[0]),
            LoopEnum::LOOP_FOREACH_HEADER_KEY_NAME => $this->variableEngine->get($symbols[2]),
            LoopEnum::LOOP_FOREACH_HEADER_VALUE_NAME => $this->variableEngine->get($symbols[4]),
        ];
    }

    protected function parseHeaderMain(string $header): string
    {
        return substr($header, strpos($header, '(') + 1, strpos($header, ')') - strpos($header, '(') - 1);
    }

    protected function parseHeader(string $expression): string
    {
        $header = substr($expression, 0, strpos($expression, ":"));
        return trim($header);
    }

    /**
     * @param string $expression
     * @return string
     */
    protected function parseBody(string $expression): string
    {
        $body = substr($expression, strpos($expression, ":") + 1);
        return trim($body);
    }

    /**
     * @param string $expression
     * @param array $loopList
     * @return array
     */
    public function scan(string &$expression, array $loopList): array
    {
        $tagId = count($loopList);
        $loopList[] = [
            LoopEnum::LOOP_FOREACH_HEADER_NAME => $this->parseHeaderData($expression),
            LexicalAnalysisEnum::SCAN_KEY_EXPRESSION_NAME => $this->parseBody($expression),
        ];
        $expression = $this->lexicalAnalysisTag->replaceTag($expression, 0, strlen($expression), LexicalAnalysisEnum::TAG_ID_LOOP, $tagId);
        return $loopList;
    }

}